﻿
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

namespace Calendars
{
	public class CalendarEventBizRules : Syrinx.BaseBizRules
	{
		public IBizObjList getCalendarEventTypes(IApplication app, IRuleContext context, object calendarId)
		{
			Swaf.BGuid.Guid realCalendarId = calendarId as Swaf.BGuid.Guid;
			if (realCalendarId == null && calendarId is string)
			{
				IResults rc2 = app.ruleMgr.simpleExecute("getArticleIdFromUniqueTitle", calendarId, "CalEventTypes");
				realCalendarId = rc2.CallResults as Swaf.BGuid.Guid;
			}
			if (realCalendarId == null)
				return app.bizObjMgr.createList("CalendarEventType");

			FlexiMap info = new FlexiMap();
			info.put("objectTreeName", "CalEventTypes");
			info.put("includeCalEType", true);
			info.put("listDetailLevel", DetailLevel.basicDetails);
			info.put("BizObjType", "CalendarEventType");

			IBizObj criteria = app.bizObjMgr.create("FilterSortOptions");
			IBizObj filter = ((IBizObjList)criteria.get("filterOptions")).insertNew();
			filter.put("field", "cal.calendarId");
			filter.put("criteria", "=");
			filter.put("value", realCalendarId);

			IResults rc = context.RuleGroup.execute(context, "getArticles", info, criteria);
			IBizObjList eventTypes = rc.CallResults as IBizObjList;
			foreach (IBizObj etype in eventTypes)
				if (!Cmn.IsEmpty(etype.get("mapImageName")))
					etype.put("mapImageUrl", string.Format("/img/{0}/MapPin/{1}", etype.get("cacheName"), etype.get("mapImageName")));
				else
					etype.put("mapImageUrl", string.Format("/img/{0}/MapPin/default.png", ActiveCompany.get("settings.defaultMediaCatalogName")));
			return eventTypes;
		}

		static Regex s_imgRegex = new Regex("/?img/(?'cacheName'[^/]+)/(?'scaling'[^/]+)/(?'imgName'.*?)(.image)?$");
		public void updateCalendarEventType(IApplication app, IRuleContext context, IBizObj eventType)
		{
			if (eventType.isDirty("mapImageUrl"))
			{
				Match m = s_imgRegex.Match((string)eventType.get("mapImageUrl"));
				if (m.Success)
				{
					eventType.put("mapImageName", m.Groups["imgName"].Value);
					eventType.put("cacheName", m.Groups["cacheName"].Value);
				}
			}
			eventType.put("treeName", "CalEventTypes");
			if (eventType.isNew)
				app.appData.put("DA.Calendar.AddCalendarEventType", eventType);
			else
				app.appData.put("DA.Calendar.UpdateCalendarEventType", eventType);

			context.RuleGroup.execute(context, "updateArticle", eventType);
		}

		public void deleteCalendarEventType(IApplication app, IRuleContext context, Swaf.BGuid.Guid eventTypeId)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", eventTypeId);
			app.appData.put("DA.Calendar.DeleteCalendarEventType", m);
			context.RuleGroup.execute(context, "deleteArticle", eventTypeId, true);
		}

		public IBizObjList getCalendarEventsInRange(IApplication app, IRuleContext context, object calendarId, FlexiMap dates)
		{
			return getCalEventsInRange(app, context, DetailLevel.basicDetails, calendarId, dates);
		}

		public IBizObjList getDetailedCalendarEventsInRange(IApplication app, IRuleContext context, object calendarId, FlexiMap dates)
		{
			return getCalEventsInRange(app, context, DetailLevel.fullDetails, calendarId, dates);
		}

		protected IBizObjList getCalEventsInRange(IApplication app, IRuleContext context, DetailLevel detail, object calendarId, FlexiMap dates)
		{
			IBizObj criteria = app.bizObjMgr.create("FilterSortOptions");
			IBizObj filter = ((IBizObjList)criteria.get("filterOptions")).insertNew();
			filter.put("field", "cal.startTime");
			filter.put("criteria", "<=");
			filter.put("value", dates.get("lastDate"));

			filter = ((IBizObjList)criteria.get("filterOptions")).insertNew();
			filter.put("field", "cal.endTime");
			filter.put("criteria", ">=");
			filter.put("value", dates.get("firstDate"));

			IBizObjList events = context.RuleGroup.execute(context, "getCalendarEvents", calendarId, criteria, detail,false,-1).CallResults as IBizObjList;
			return events;
		}

		public IBizObjList getCalendarEvents(IApplication app, IRuleContext context, object calendarId, IBizObj criteria, DetailLevel detail, bool includeLocations, int maxElements)
		{
			Swaf.BGuid.Guid realCalendarId = calendarId as Swaf.BGuid.Guid;
			if (realCalendarId == null && calendarId is string)
			{
				IResults rc2 = app.ruleMgr.simpleExecute("getArticleIdFromUniqueTitle", calendarId, "Calendars");
				realCalendarId = rc2.CallResults as Swaf.BGuid.Guid;
			}
			if (realCalendarId == null)
				return app.bizObjMgr.createList("CalendarEvent");

			if (criteria == null)
				criteria = app.bizObjMgr.create("FilterSortOptions");

			bool skipAdd = false;
			IBizObjList filters = (IBizObjList)criteria.get("filterOptions");
			foreach (IBizObj f in filters)
			{
				if (!skipAdd)
					skipAdd = "cal.calendarId".CompareTo(f.get("field")) == 0;
				if("cal.startTime".CompareTo(f.get("field")) == 0)
				{
					DateTime n = DateTime.Now;
					if(string.Compare(f.get("value").ToString(), "today", true) == 0)
						f.put("value", new DateTime(n.Year, n.Month, n.Day));
				}
			}
			if (!skipAdd)
			{
				IBizObj filter = ((IBizObjList)criteria.get("filterOptions")).insertNew();
				filter.put("field", "cal.calendarId");
				filter.put("criteria", "=");
				filter.put("value", realCalendarId);
			}

			skipAdd = true;
			IBizObjList sorts = (IBizObjList)criteria.get("sortOptions");
			foreach(IBizObj s in sorts)
				if((skipAdd = "cal.startTime".CompareTo(s.get("field"))==0))
					break;

			if (!false)
			{
				IBizObj sort = ((IBizObjList)criteria.get("sortOptions")).insertNew();
				sort.put("field", "cal.startTime");
			}

			FlexiMap info = new FlexiMap();
			info.put("objectTreeName", "CalEvents");
			info.put("BizObjType", "CalendarEvent");
			info.put("calendarId", calendarId);
			info.put("criteria", criteria);
			info.put("listDetailLevel", detail);
			info.put("includeCalEvt", true);
			info.put("maxElements", maxElements);
			if (includeLocations)
				info.put("includeLocations", true);

			IResults rc = context.RuleGroup.execute(context, "getArticles", info, criteria);
			IBizObjList calEvents = rc.CallResults as IBizObjList;
			return calEvents;
		}

		public IBizObj getCalendarEventDetails(IApplication app, IRuleContext context, Swaf.BGuid.Guid eventId)
		{
			FlexiMap info = new FlexiMap();
			info.put("eventId", eventId);
			IBizObj calEvent = app.appData.get("DA.Calendar.GetCalendarEventDetails", info) as IBizObj;
			if (calEvent != null &&  Cmn.IsTrue(calEvent.get("type.allowEnrollment")))
			{
				if ((int)calEvent.get("maxCapacity") == -1)
				{
					calEvent.put("openEnrollment", true);
					calEvent.put("maxCapacity", 0);
					calEvent.clearDirtyFlags();
				}
				IBizObjList enrolledUsers = app.appData.get("DA.Calendar.GetCalendarEventEnrolled", info) as IBizObjList;
				if (enrolledUsers != null)
				{
					calEvent.put("enrolled", enrolledUsers);
					calEvent.put("currentEnrollment", enrolledUsers.numElements);
				}
			}
			if (calEvent != null && calEvent.get("location.id") != null)
				((IBizObj)calEvent.get("location")).isNew = false;
			return calEvent;
		}

		public bool isUserEnrolledInEvent(IApplication app, IRuleContext context, Swaf.BGuid.Guid eventId, Swaf.BGuid.Guid userId)
		{
			FlexiMap info = new FlexiMap();
			info.put("eventId", eventId);
			info.put("userId", userId);

			IBizObj user = app.appData.get("DA.Calendar.GetIsUserEnrolled", info) as IBizObj;
			return user != null;
		}

		/// <summary>
		/// will update the calendar event and the article for the provided bizobj
		/// </summary>
		/// <remarks>The calEvent inherits from article to provide the title, body and other information 
		/// for the given event.  However, the database separates the calendar id from the article id so that
		/// a given article can be used as the title/body for multple calendar events.  This is good for things
		/// like classes or other events that are scheduled over and over again in the calendar.  
		/// When an update occurs on a calendar event, if the event is just being updated, it looks to see
		/// if the articleId and the calendarEvent's id are the same.  If they are it just updates the article by
		/// call the bizrule.  However, if they are not the same it will create a new article and set its id to the
		/// calendar event's id and change the source article id in the db to the new one created.  
		/// 
		/// When a calendar event's title/subject is coming from another source article, the user cannot edit that
		/// origional source article from within the calendar event.  They would actually be creating a new article
		/// so that not all the other calendar events are updated with the changes made on the one.  If the user
		/// wants to update the source article for all of the calender events that are referencing it, they will
		/// edit the source article directly.</remarks>
		/// <param name="app"></param>
		/// <param name="context"></param>
		/// <param name="calEvent"></param>
		public void updateCalendarEvent(IApplication app, IRuleContext context, IBizObj calEvent)
		{
			calEvent.put("treeName", "CalEvents");
			if(Cmn.IsTrue(calEvent.get("type.allowEnrollment")) && Cmn.IsTrue(calEvent.get("openEnrollment")))
				calEvent.put("maxCapacity", -1);

			if (calEvent.isNew || calEvent.get("location.id") == null)
			{
				((IBizObj)calEvent.get("location")).isNew = true;
				calEvent.put("location.id", new Swaf.BGuid.Guid());
			}
			if (calEvent.isNew)
			{
				app.appData.put("DA.Calendar.AddCalendarEvent", calEvent);
			}
			else
			{
				app.appData.put("DA.Calendar.UpdateCalendarEvent", calEvent);
			}

			context.RuleGroup.execute(context, "updateAddress", calEvent.get("location"));
			context.RuleGroup.execute(context, "updateArticle", calEvent);
		}

		public void deleteCalendarEvent(IApplication app, IRuleContext context, Swaf.BGuid.Guid calEventId)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", calEventId);
			app.appData.put("DA.Calendar.DeleteCalendarEvent", m);
			context.RuleGroup.execute(context, "deleteArticle", calEventId, true);
		}

		public IBizObjList getCalendars(IApplication app, IRuleContext context)
		{
			FlexiMap info = new FlexiMap();
			info.put("objectTreeName", "Calendars");
			info.put("listDetailLevel", DetailLevel.basicDetails);
			info.put("includeCal", true);
			info.put("BizObjType", "Calendar");

			IResults rc = context.RuleGroup.execute(context, "getArticles", info, null);
			IBizObjList calendars = rc.CallResults as IBizObjList;
			return calendars;
		}

		public void updateCalendar(IApplication app, IRuleContext context, IBizObj calendar)
		{
			calendar.put("treeName", "Calendars");
			if (calendar.isNew)
				app.appData.put("DA.Calendar.AddCalendar", calendar);
			else
				app.appData.put("DA.Calendar.UpdateCalendar", calendar);
			IResults rc = context.RuleGroup.execute(context, "updateArticle", calendar);
		}

		public void deleteCalendar(IApplication app, IRuleContext context, Swaf.BGuid.Guid calendarId)
		{
			FlexiMap m = new FlexiMap();
			m["id"] = calendarId;
			app.appData.put("DA.Calendar.DeleteCalendar", m);
			IResults rc = context.RuleGroup.execute(context, "deleteArticle", calendarId, true);
		}

		/// <summary>
		/// This bizrule is called by the UserLogin feature when it needs to get the list of avilable app
		/// functions for users of the application.
		/// This bizrule returns a CSF security AppFunction bizobjlist that is derived from the current user's
		/// active company's article groups.  Each article group in the application is used to define a corrisponding
		/// Security privilege which is exposed as an AppFunction.
		/// </summary>
		/// <remarks>All of the articles groups for a given company become a part of a single group of
		/// application functions.  When an administrator is configuring the security access rights to various
		/// user groups each article group will be shown in the application function list and the administrator
		/// will be able to specify the access privilege for the article group.  If a user is given a privilege
		/// of None for a given article group, they will not be able to see it.  If a user is given a privilege
		/// of Delete, they will be able to delete the article group, add new article groups and add article set in the
		/// article group.</remarks>
		/// <param name="specifierId">The article group AppFunction specifier id.</param>
		/// <returns></returns>
		public IBizObjList getCalendarAppFunctions(IApplication app, IRuleContext context, string specifierId)
		{
			try
			{
				FlexiMap m = new FlexiMap();
				if (specifierId == null)
					specifierId = "CalendarAppFunctions";
				m.put("specifierId", specifierId);
				m.put("companyId", ActiveCompanyId);
				m.put("treeName", "Calendars");
				return (IBizObjList)app.appData.get("DA.Calendar.GetCalAppFunctions", m);
			}
			catch (Exception e)
			{
				throw new BaseException("Error executing getArticleGroupAppFunctions bizRule.", e);
			}
		}
	}
}
